#include <asm/io.h>
#include <asm/regs.h>
#include <asm/system.h>
#include <cnix/wait.h>
#include <cnix/partition.h>
#include <cnix/fs.h>
#include <cnix/kernel.h>
#include <cnix/driver.h>
#include <cnix/list.h>
#include <cnix/string.h>
#include <cnix/mm.h>


#define MAX_RAMDISK_BLOCK_NR	(4 * 1024)		// Max ramdisk size 4M

#define RAMDISK_SIZE_ADDR	(0x7c00 + 502)

static char *ramdisk_mem_begin;
static int ramdisk_block_nr;		// BLOCKSIZ

int ramdisk_open(dev_t major, dev_t minor, int flags)
{
	if (major != RAMDISK_MJ_DEV)
	{
		DIE("BUG: cannot happen\n");
	}

	if (minor != 0)
	{
		return -1;
	}

	return 0;
}

/* 
 * The bootloader has put the ramdisk content at the last 4M memory
 */
void ramdisk_init(unsigned long *end_mem)
{
	int *p;
	
	p = (int *)(RAMDISK_SIZE_ADDR);

	if (*p == 0)
	{
		ramdisk_block_nr = 0;
		ramdisk_mem_begin = NULL;

		return;
	}
	
	if (*end_mem <= (MAX_RAMDISK_BLOCK_NR << BLOCKSIZ_SHIFT))
	{
		DIE("memory is too small for cnix, giving up...");
	}

	ramdisk_block_nr = *p;

	if (ramdisk_block_nr % BLOCKSIZ)
	{
		PRINTK("Ramdisk size (%x) error", ramdisk_block_nr);
		
		ramdisk_block_nr = 0;
		ramdisk_mem_begin = NULL;
		
		return;
	}
	
	ramdisk_block_nr >>= BLOCKSIZ_SHIFT;
	*end_mem -= (MAX_RAMDISK_BLOCK_NR << BLOCKSIZ_SHIFT);
	ramdisk_mem_begin = (char *)(*end_mem);

}

size_t ramdisk_size(dev_t major, dev_t minor)
{
	if (major != RAMDISK_MJ_DEV)
	{
		DIE("BUG: cannot happen\n");
	}
	
	if (minor != 0)
	{
		DIE("bad device ... %d!\n", minor);
		return 0;
	}

	return ramdisk_block_nr << BLOCKSIZ_SHIFT;
}


int ramdisk_rw_block(int rw_mode, struct buf_head * buf)
{
	dev_t dev;
	char *ptr;

	dev = MINOR(buf->b_dev);
	if (dev >= 1)
	{
		PRINTK("ramdisk: bad device %d!\n", dev);
		buf->b_flags |= B_ERROR;
		return 0;
	}

	if (buf->b_blocknr >= ramdisk_block_nr || buf->b_blocknr < 0)
	{
		buf->b_flags |= B_ERROR;
		PRINTK("ramdisk: bad block: %d\n", buf->b_blocknr);
		return 0;
	}

	ptr = ramdisk_mem_begin + (buf->b_blocknr << BLOCKSIZ_SHIFT);

	if (rw_mode == READ)
	{
		memcpy(buf->b_data, ptr, BLOCKSIZ);
	}
	else if (rw_mode == WRITE)
	{
		memcpy(ptr, buf->b_data, BLOCKSIZ);
	}
	else
	{
		DIE("impossible, giving up...");
	}

	buf->b_flags |= B_DONE;
	
	return 0;
}
